global Total := Array()
SysGet, MonitorCount, MonitorCount
SysGet, MonitorName, MonitorName

EnvGet, UserProfile, UserProfile
global ConfigDir := UserProfile . "\.vlassist"
global SysClipDir := A_ScriptDir . "\clip"
global UserClipDir := ConfigDir . "\clip"
global LogPath := ConfigDir . "\log"
global ConfigFile := ConfigDir . "\config.json"
global ErrorLogFile := LogPath . "\error.log"
global LogFile := LogPath . "\assist.log"
global GenConfigScript := A_ScriptDir . "\assist_genconfig.ps1"
global GenHelpScript := A_ScriptDir . "\assist_genhelp.ps1"
global HelpFile := A_ScriptDir . "\help.html"


FileCreateDir, %UserClipDir%
FileCreateDir, %LogPath%

if not FileExist(ConfigFile){
    FileCopy, %A_ScriptDir%\config.json.template, %ConfigFile%
}

FileRead, ConfigContent, %ConfigFile%

Config := JSON.Load(ConfigContent)

global Collections := Config.Collections

WriteLog(log){
    FileAppend, %log%, %LogFile%, UTF-8
}


getDesktopHeight()
{
	WinGetPos,,, tbW, tbH, ahk_class Shell_TrayWnd
	return A_ScreenHeight - tbH
}

OpenDirectory(Dir){
    directory := Dir.Path
    name := Dir.Name
    if ! FileExist(directory)
    {
        TrayTip, Error, File Not Exist %directory%
        Sleep,3000
        TrayTip
        Return
    }
    Run Explore %directory%
    TrayTip, Info, Open Directory directory %name%
    Sleep,3000
    TrayTip
}


CheckFile(File){
    if ! FileExist(File)
    {
        FileAppend, Error: file does not exist : %File% `n, %ErrorLogFile%, UTF-8
        return false
    }
    return true
}


RunExe(App)
{
    Filter := App.Filter
    Executable := App.Path
    WorkDir := App.WorkDir
    Name := App.Name
    InitStatus := App.InitStatus
    SelectHide := App.SelectHide
    HalfScr := App.HalfScr
    MaxOnStart := App.MaxOnStart

    if ! FileExist(Executable)
    {
        TrayTip,Error, File Not Exist -> %Name%
        Sleep,3000
        TrayTip
        Return
    }
    if(InitStatus="Hide" || SelectHide = true)
    {
        DetectHiddenWindows, On
    }
    if WinExist(Filter){

        if(winActive(Filter))
        {
            TrayTip, Info, Minimize %Name%
            PostMessage, 0x112, 0xF020,,, %Filter%,
            WinMinimize,%Filter%
        }
        else
        {
            TrayTip, Info, Activate %Name%
            WinRestore
			WinWait,%Filter%,,15
            WinActivate
			if(HalfScr = true){
				WinMove, %Filter%,, 0, 0, A_ScreenWidth/2, getDesktopHeight()
			}
			else{
				WinMaximize,%Filter%
			}
        }
        if(InitStatus="Hide" || SelectHide = true)
        {
            DetectHiddenWindows, Off
        }
        TrayTip
    }
    else{
        if(InitStatus="Hide" || SelectHide = true)
        {
            DetectHiddenWindows, Off
        }
        TrayTip, Info,Start %Name%
        Run,%Executable%,%WorkDir%,%InitStatus%
		WinWait,%Filter%,,15
		WinActivate
		if(HalfScr = true){
			WinMove, %Filter%,, 0, 0, A_ScreenWidth/2, getDesktopHeight()
		}
		if(MaxOnStart=true)
		{
			WinMaximize,%Filter%
		}
        TrayTip
        return
    }
    return
}

RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec(ComSpec " /C " command)
    return exec.StdOut.ReadAll()
}

ClipCopy(filepath) {
    if ! FileExist(filepath)
    {
        TrayTip,Error, File Not Exist -> %Name%
        Sleep,3000
        TrayTip
        Return
    }
    RunWaitOne("clip < " . filepath)
    TrayTip, Info, %filename% has been copyed to clipboard
    Sleep,3000
    TrayTip
}


OpenUrl(Location){
    Path := Location.Path
    Name := Location.Name
    TrayTip, Info, Opening Url %Name%
    run, Chrome.exe %Path%, Max
    Sleep,3000
    TrayTip
}

RunBash(Script){
    BashExe := Bash.Path
    File := Script.Path
    if ! FileExist(File)
    {
        TrayTip,Error, File Not Exist -> %File%
        Sleep,3000
        TrayTip
        Return
    }
    Run, bash.exe "%File%"
}

RunPowerShell(Script){
    File := Script.Path

    if ! FileExist(File)
    {
        TrayTip,Error, File Not Exist -> %File%
        Sleep,3000
        TrayTip
        Return
    }
    Run, PowerShell.exe -ExecutionPolicy Bypass -File %File%
}

RunCmd(Script){
    File := Script.Path
    if ! FileExist(File)
    {
        TrayTip,Error, File Not Exist -> %File%
        Sleep,3000
        TrayTip
        Return
    }
    Run, cmd.exe /c "%File%"
}

RunLink(Link){
    File := Link.Path
    if ! FileExist(File)
    {
        TrayTip,Error, File Not Exist -> %File%
        Sleep,3000
        TrayTip
        Return
    }
    Run, %File%, Max
}

OpenFile(File){
    File := File.Path
    if ! FileExist(File)
    {
        TrayTip,Error, File Not Exist -> %File%
        Sleep,3000
        TrayTip
        Return
    }
    Run, "%File%",Max
}

RunElement(Element){
    if(Element.Type = "App")
    {
        RunExe(Element)
    } 
    else if (Element.Type = "Dir")
    {
        OpenDirectory(Element)
    } 
    else if (Element.Type = "Web")
    {
        OpenUrl(Element)
    }
    else if (Element.Type = "Link")
    {
        RunLink(Element)
    }
    else if (Element.Type = "Bash")
    {
        RunBash(Element)
    }
    else if (Element.Type = "Cmd")
    {
        RunCmd(Element)
    }
    else if (Element.Type = "Powershell")
    {
        RunPowerShell(Element)
    }
    else if (Element.Type = "File")
    {
        OpenFile(Element)
    }
    else if (Element.Type = "Clip")
    {
        ClipCopy(Element.Path)
    }
    else{
        Name := Element.Name
        MsgBox, 0, Error, Open %Name% error with type invalid ... error, 3
    }
}

HotKey(Key){
    for index, element in Total
    {
        if(element.Key = Key){
            RunElement(element)
            break
        }
    }
}

LoopClip(Path, ByRef Result){
    WriteLog("Loop Clip Path: " . Path . "`n")
    if ! CheckFile(Path)
    {
        return
    }
    Loop, Files, %Path%\*, F
    {
        Result.push({ Path: A_LoopFileFullPath, Name: "@" . A_LoopFileName, Type: "Clip" })
    }
}

LoopPath(Root, Type, ByRef Result){
    Ext := "*.lnk"
    if( Type = "Link" ){
        Ext := "*.lnk"
    }
    else if( Type = "Markdown" )
    {
        Ext := "*.md"
    }
    else if( Type = "Script" ){
        Ext := "*.sh"
    }
    else
    {
        Type := "Link"
    }
    Location := Root.Path
    if !CheckFile(Location){
        return
    }
    Loop Files, %Location%\%Ext%, R
    {
        if(Type = "Link"){
            if(0 <> InStr(A_LoopFileName, "uninstall") or 0 <> InStr(A_LoopFileName, "卸载"))
                continue
        }
        if(Type = "Markdown"){
            if(0 <> InStr(A_LoopFileName, "readme") or 0 <> InStr(A_LoopFileName, "changelog") or 0 <> InStr(A_LoopFileName, "history") or 0 <> InStr(A_LoopFileName, "licence") or 0 <> InStr(A_LoopFileName, "history") or 0 <> InStr(A_LoopFileName, "desc") or 0 <> InStr(A_LoopFileName, "license"))
                continue
            if(0 <> InStr(A_LoopFileFullPath, "node_modules")){
                continue
            }
        }
        Result.Push({ Name: A_LoopFileName
            , Type: Type
            , Top: Fasle
            , Path: A_LoopFileFullPath})
        ; WriteLog("Add File to Index: " . A_LoopFileName . "`n")
    }
}

; excludes: .git,tools,node_modules,.idea,.vscode
StackLoop(path, exts, excludes) {
    result := Array()

    if ! CheckFile(path)
    {
        return result
    }

    st := Object(1, path)
    Loop
    {
        path := st.Remove()
        Loop %path%\*.*, 1
        {
            if A_LoopFileAttrib contains H,S
                continue
            if A_LoopFileAttrib contains D
            {
                if A_LoopFileName in %excludes%
                    continue
                st.Insert(A_LoopFileFullPath)
                continue
            }
            Type := "Link"
            StringLower, loopext, A_LoopFileExt

            ShouldAdd := False
            if loopext in %exts%
            {
                ShouldAdd := True
            }
            if(loopext = exts)
            {
                ShouldAdd := True
            }

            if( ShouldAdd = True ){
                if(loopext = "lnk"){
                    Type := "Link"
                }
                else if(loopext = "sh"){
                    Type := "Bash"
                }
                else if(loopext = "cmd"){
                    Type := "Cmd"
                }
                else if(loopext = "bat"){
                    Type := "Cmd"
                }
                else if(loopext = "ps1"){
                    Type := "Powershell"
                }
                else {
                    Type := "File"
                }
                if(Type = "Link"){
                    if(0 <> InStr(A_LoopFileName, "uninstall") or 0 <> InStr(A_LoopFileName, "卸载"))
                        continue
                }
                if(loopext = "md"){
                    if(0 <> InStr(A_LoopFileName, "readme") or 0 <> InStr(A_LoopFileName, "changelog") or 0 <> InStr(A_LoopFileName, "history") or 0 <> InStr(A_LoopFileName, "licence") or 0 <> InStr(A_LoopFileName, "history") or 0 <> InStr(A_LoopFileName, "desc") or 0 <> InStr(A_LoopFileName, "license"))
                        continue
                }
                if loopext in %exts%
                {
                    if(SubStr(A_LoopFileName, 1, 1) = "."){
                        continue
                    }
                    if(loopext = "pdf" and 0 <> RegExMatch(A_LoopFileName, "^image\d+.pdf$"))
                    {
                        continue
                    }
                    result.Push({ Name: A_LoopFileName
                            , Type: Type
                            , Ext: loop
                            , Top: Fasle
                            , Path: A_LoopFileFullPath})
                }
            }
        }
    }
    until st.MaxIndex() = ""
    return result
}

OpenHelp(){
    Run, %HelpFile%
}

GenConfig(){
    RunWait, PowerShell.exe -ExecutionPolicy Bypass -File %GenConfigScript%,,Hide
}

GenHelp(){
    RunWait, PowerShell.exe -ExecutionPolicy Bypass -File %GenHelpScript%,,Hide
}

RebuildIndex(){

    Result := Array()
    StartTime := A_TickCount
    FileDelete, %LogPath%

    Collections.push({ Type: "Clip.Path", Name: "System Clip Path", Path: SysClipDir})
    Collections.push({ Type: "Clip.Path", Name: "User Clip Path", Path: UserClipDir})
    
    For index, element in Collections
    {
        Type := element.Type
        if( Type <> "Web" )
        {
            CheckFile(element.Path)
        }

        if( element.Type = "Clip.Path")
        {
            LoopClip(element.Path, Result)
        }
        else if( 0 <> InStr(element.Type, ".Root"))
        {
            WriteLog("Loop Path: '" . element.Path . "' with type " . Type . "`n")
            Items := StackLoop(element.Path, element.Ext, ".git,tools,node_modules,.idea,.vscode")
            Loop % Items.length(){
                Result.push(Items.pop())
            }
        }
        else
        {
            WriteLog("Push Element: " . element.Name . " = " . element.Path . "`n")
            Result.Push(element)
        }
    }
    SpendTime := A_TickCount - StartTime
    WriteLog("Rebuild Index Spends Time: " . SpendTime . " And Total Item Count is " . Result.Length() . "`n")
    return Result
}


InitAhk(){
    SetWorkingDir, %A_ScriptDir%
    FileDelete, %ErrorLogFile%
    FileDelete, %LogFile%
    GenConfig()
    GenHelp()
    Total := RebuildIndex()
    if FileExist(ErrorLogFile){
        Run, %ErrorLogFile%
    }
}


InitAhk()
